package com.tiancheng.trade.authserver.service.impl;

import cn.hutool.core.lang.ObjectId;
import cn.hutool.extra.pinyin.PinyinUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tiancheng.trade.authserver.dto.ApplicationAddUpdateDTO;
import com.tiancheng.trade.authserver.dto.ApplicationListDTO;
import com.tiancheng.trade.authserver.entity.AuthApplication;
import com.tiancheng.trade.authserver.entity.AuthPermission;
import com.tiancheng.trade.authserver.entity.AuthRole;
import com.tiancheng.trade.authserver.enums.ThirdPlatformEnums;
import com.tiancheng.trade.authserver.mapper.AuthApplicationMapper;
import com.tiancheng.trade.authserver.model.query.ApplicationBaseInfoUpdateParam;
import com.tiancheng.trade.authserver.model.query.ApplicationQuery;
import com.tiancheng.trade.authserver.model.reponse.ApplicationBaseInfo;
import com.tiancheng.trade.authserver.service.IAuthApplicationService;
import com.tiancheng.trade.authserver.service.IAuthPermissionService;
import com.tiancheng.trade.authserver.service.IAuthRoleService;
import com.tiancheng.trade.authserver.service.IAuthRoleMapPermissionService;
import com.tiancheng.trade.commom.web.configuration.PlatformRequestProperties;
import com.tiancheng.trade.commom.core.data.PageInfo;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @Author: likailun
 * @Description:
 * @Date: create in 2024/10/22 11:04
 */
@Service
public class AuthApplicationServiceImpl extends ServiceImpl<AuthApplicationMapper, AuthApplication> implements IAuthApplicationService {
    private static final Object loadThisApplicationIdLock = new Object();
    /**
     * 本系统的 application id
     */
    private static volatile Long thisApplicationId;
    @Resource
    @Lazy
    private IAuthPermissionService permissionService;
    @Resource
    private PlatformRequestProperties platformRequestProperties;
    @Resource
    @Lazy
    private IAuthRoleService roleService;
    @Resource
    @Lazy
    private IAuthRoleMapPermissionService roleMapPermissionService;
    @Resource
    @Lazy
    private IAuthOrgService orgService;

    @Override
    public AuthApplication getByTypeAndClientId(ThirdPlatformEnums type, String clientId) {

        List<AuthApplication> tas = this.list(new LambdaQueryWrapper<AuthApplication>().eq(AuthApplication::getClientId,clientId).eq(AuthApplication::getType,type.name()));
        if (tas != null && !tas.isEmpty()) {
            return tas.get(0);
        }
        return null;
    }

    @Override
    public PageInfo<ApplicationListDTO> queryPage(ApplicationQuery param) {
        IPage<AuthApplication> pageQuery=new Page<>(param.getCurrentPage(),param.getPageSize());
        final IPage<AuthApplication> authApplicationIPage = this.baseMapper.selectPage(pageQuery, new LambdaQueryWrapper<AuthApplication>().eq(Objects.nonNull(param.getId()), AuthApplication::getId, param.getId())
                .like(StringUtils.isNotEmpty(param.getName()), AuthApplication::getName, param.getName())
                .eq(StringUtils.isNotEmpty(param.getClientId()), AuthApplication::getClientId, param.getClientId())
                .eq(Objects.nonNull(param.getType()), AuthApplication::getType, param.getType())
                .like(StringUtils.isNotEmpty(param.getProjectName()), AuthApplication::getProjectName, param.getProjectName()));

        final PageInfo<ApplicationListDTO> page = new PageInfo<>();
        final PageInfo.Pagination pagination = new PageInfo.Pagination(authApplicationIPage.getCurrent(), (int) authApplicationIPage.getSize(), authApplicationIPage.getTotal());
        page.setPagination(pagination);
        if(authApplicationIPage.getTotal()>0){
            final List<ApplicationListDTO> list = new ArrayList<>();
            for (AuthApplication record : authApplicationIPage.getRecords()) {
                final ApplicationListDTO applicationListDTO = new ApplicationListDTO();
                BeanUtils.copyProperties(record,applicationListDTO);
                list.add(applicationListDTO);
            }
            page.setList(list);
        }
        return page;
    }

    @Override
    public Long saveByDTO(ApplicationAddUpdateDTO param) {
        param.setId(null);
        AuthApplication application = new AuthApplication();
        application.setDataPermissionCode(this.permissionService.getDataPermissionCode(StringUtils.isNotEmpty(param.getDataPermissionId())?Long.valueOf(param.getDataPermissionId()):null));
        BeanUtils.copyProperties(param, application);
        if(StringUtils.isBlank(application.getProjectName())) {
            application.setProjectName(application.getName());
        }
        AuthApplication oldApp = getByProjectName(application.getProjectName());
        if(null!=oldApp){
            throw new RuntimeException("项目名称重复");
        }
        String clientId = param.getClientId();
        if (clientId == null || clientId.isEmpty()) {
            clientId = ObjectId.next();
        }
        String clientSecret = param.getClientSecret();
        if (clientSecret == null || clientSecret.isEmpty()) {
            clientSecret = DigestUtils.md5Hex(UUID.randomUUID().toString());
        }
        application.setClientId(clientId);
        application.setClientSecret(clientSecret);
        application.setCreatedDt(LocalDateTime.now());
        this.save(application);

        afterAddApplication(application);

        return application.getId();
    }

    @Override
    public void updateBaseInfo(ApplicationBaseInfoUpdateParam param) {
        AuthApplication application = this.getById(param.getId());

        String oldDataPermissionCode = application.getDataPermissionCode();
        boolean forceInvalidToken = false;
        if (StringUtils.isEmpty(param.getClientSecret())) {
            BeanUtils.copyProperties(param, application, "clientId", "clientSecret");
        } else {
            if (StringUtils.isNotEmpty(param.getClientId()) && !param.getClientId().equals(application.getClientId())) {
                forceInvalidToken = true;
            }
            if (StringUtils.isNotEmpty(param.getClientSecret())
                    && !param.getClientSecret().equals(application.getClientSecret())) {
                forceInvalidToken = true;
            }
            if (StringUtils.isNotEmpty(param.getClientPublicKey())
                    && !param.getClientPublicKey().equals(application.getClientPublicKey())) {
                forceInvalidToken = true;
            }
            if (StringUtils.isNotEmpty(param.getPlatformPublicKey())
                    && !param.getPlatformPublicKey().equals(application.getPlatformPublicKey())) {
                forceInvalidToken = true;
            }
            BeanUtils.copyProperties(param, application);
        }
        //设置新数据权限
        String dataPermissionId = param.getDataPermissionId();
        if (StringUtils.isNotEmpty(dataPermissionId)) {
            application.setDataPermissionCode(this.permissionService.getDataPermissionCode(Long.valueOf(dataPermissionId)));
        }
        updateById(application);
        if (!oldDataPermissionCode.equals(application.getDataPermissionCode())) {
            this.roleService.updateDataPermissionCode(application);
            this.permissionService.updateDataPermissionCode(application);
            this.orgService.updateDataPermissionCode(application);
        }
        if (forceInvalidToken) {
            //this.authTokenService.forceInvalid(param.getId());
        }
    }

    @Override
    public ApplicationBaseInfo getBaseInfo(String id) {
        List<AuthApplication> list = this.list(new LambdaQueryWrapper<AuthApplication>().eq(AuthApplication::getClientId, id));
        if(CollectionUtils.isNotEmpty(list)){
            ApplicationBaseInfo app = new ApplicationBaseInfo();
            app.setClientId(list.get(0).getClientId());
            app.setName(list.get(0).getName());
            app.setProjectName(list.get(0).getProjectName());
            app.setIconUrl(list.get(0).getIconUrl());
            return app;
        }
        return null;
    }

    @Override
    public void delete(String id) {

    }

    @Override
    public Long getThisApplicationId() {
        if (AuthApplicationServiceImpl.thisApplicationId == null) {
            synchronized (AuthApplicationServiceImpl.loadThisApplicationIdLock) {
                if (AuthApplicationServiceImpl.thisApplicationId == null) {
                    AuthApplication thisApplication = this.getByTypeAndClientId(ThirdPlatformEnums.tiancheng,
                            this.platformRequestProperties.getClientId());
                    AuthApplicationServiceImpl.thisApplicationId = thisApplication.getId();
                }
            }
        }
        return AuthApplicationServiceImpl.thisApplicationId;
    }

    @Override
    public AuthApplication getByProjectName(String projectName) {
        final List<AuthApplication> applications = this.list(new LambdaQueryWrapper<AuthApplication>().eq(AuthApplication::getProjectName, projectName));
        return CollectionUtils.isNotEmpty(applications)?applications.get(0):null;
    }

    /**
     * 添加应用后续处理
     * 如有菜单的：
     * 1、根据菜单menuJson.menu字段对应创建权限项
     * 2、创建一个管理员角色，授权所有权限
     * @param application
     */
    private void afterAddApplication(AuthApplication application) {
        String menuJson = application.getMenuJson();
        if(StringUtils.isNotBlank(menuJson) && menuJson.trim().startsWith("[")) {
            JSONArray jsonArray = JSON.parseArray(menuJson);
            Map<String, AuthPermission> permMap = new HashMap<>();
            parseMenuJson(jsonArray, permMap, application);

            final Collection<AuthPermission> permissionsSave = permMap.values();
            permissionService.saveBatch(permissionsSave);

            AuthRole role = new AuthRole();
            role.setRoleName(application.getProjectName()+":管理员");
            role.setRoleCode(PinyinUtil.getFirstLetter(application.getProjectName(), "") + ":admin");
            role.setDescribe(role.getRoleName());
            role.setApplicationId(application.getId().toString());
            role.setDataPermissionCode(application.getDataPermissionCode());
            role.setParentId(0L);
            role.setParentIds(";");
            roleService.save(role);
            roleMapPermissionService.saveRoleMapPermission(role, new HashSet<>(permissionsSave));
        }
    }

    /**
     * 解析json数组的菜单到permission中
     * @param menuList
     * @param permissionMap
     * @param application
     */
    private void parseMenuJson(List menuList, Map<String, AuthPermission> permissionMap, AuthApplication application) {
        for (Object o : menuList) {
            if(o instanceof Map) {
                AuthPermission permission = new AuthPermission();
                Object menu = ((Map) o).get("menu");
                Object name = ((Map) o).get("name");
                Object children = ((Map) o).get("children");
                if(menu != null) {
                    permission.setCode(menu.toString());
                }
                if(name != null) {
                    permission.setName(name.toString());
                    permission.setDescribe(name.toString());
                }
                permission.setApplicationId(application.getId());
                permission.setDataPermissionCode(application.getDataPermissionCode());
                permissionMap.put(permission.getCode(), permission);
                if(children != null && children instanceof List) {
                    parseMenuJson((List) children, permissionMap, application);
                }
            }
        }
    }
}
